<!DOCTYPE html>

<html>

<head>
    <title>code-editor</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <link href="/static/icon/icon.svg" rel="icon" type="image/x-icon">
    <style>
        body {
            margin: 0px;
            padding: 0px;
        }
        
        .main {
            position: absolute;
            top: 0px;
            bottom: 0px;
            left: 0px;
            right: 0px;
        }
        
        .header {
            text-align: center;
            font-size: 1.5rem;
            margin-top: 10px;
            font-weight: bolder;
        }
        
        .inline {
            display: inline-block;
        }
        
        .left-editor-block,
        .right-editor-block {
            vertical-align: top;
            height: 100%;
        }
        
        .left-editor-block {
            width: 50%;
        }
        
        .right-editor-block {
            width: 49%;
        }
        
        iframe {
            width: 100%;
            height: 100%;
            border-width: 0px;
        }
        
        .flex {
            display: flex;
            flex-direction: column;
            height: 100%;
            width: 100%;
        }
        
        .editor-header {
            text-align: center;
            margin: 10px;
            font-size: 1.1rem;
        }
        
        .editor {
            width: 100%;
            border: 1px solid grey;
            flex-grow: 1;
        }
        
        .tool {
            text-align: center;
            margin: 10px 0px 5px 0px;
            height: 50px;
        }
        
        .tool button {
            margin: 0px 1%;
            font-size: 1.0rem;
            width: 30%;
        }
        
        pre {
            word-wrap: break-word;
        }
    </style>
    <script src="https://7072-prd-ffx26-1255301037.tcb.qcloud.la/321zou.com/dsl/jquery/jquery-3.5.1.min.js?v=238619"></script>
    <script src="https://7072-prd-ffx26-1255301037.tcb.qcloud.la/321zou.com/dsl/monaco-editor/min/vs/loader.js"></script>

    <script src="/runtime/javascript/OpenDSLRuntime.js"></script>
    <script src="/mathjs/math.js"></script>
</head>

<body>
    <div class="main">
        <div class="inline left-editor-block">
            <div class="flex">

                <div class="editor-header"><span>代码(Code)</span></div>
                <!-- <div class="tool">
            </div> -->
                <div id="dslCode" class="editor inline"></div>

                <div class="tool">
                    <!-- <button onclick="save()">保存</button> -->
                    <button id="parseBtn" onclick="parse()">解析<br/>(Parse)</button>
                    <button id="executeBtn" onclick="execute()">执行<br/>(Execute)</button>
                    <button id="showHelpBtn" onclick="showHelp()">帮助<br/>(Help)</button>
                </div>

            </div>
        </div>
        <div class="inline right-editor-block">
            <iframe id="iframe" src="help.html"></iframe>
        </div>
    </div>

    <script>
        require.config({
            paths: {
                vs: 'https://7072-prd-ffx26-1255301037.tcb.qcloud.la/321zou.com/dsl/monaco-editor/min/vs'

            }
        });

        var dslCodeEditor = null;

        require(['vs/editor/editor.main'], function() {
            // Register a new language
            monaco.languages.register({
                id: 'dsl'
            });

            // Register a tokens provider for the language
            monaco.languages.setMonarchTokensProvider('dsl', {
                // Set defaultToken to invalid to see what you do not tokenize yet
                // defaultToken: 'invalid',

                keywords: [
                    'abstract', 'continue', 'for', 'new', 'switch', 'assert', 'goto', 'do',
                    'if', 'private', 'this', 'break', 'protected', 'throw', 'else', 'public',
                    'enum', 'return', 'catch', 'try', 'interface', 'static', 'class',
                    'finally', 'const', 'super', 'while', 'true', 'false',
                    'var', 'function', 'let',
                    '定义', '抓手', '赋能', '如果', '再如果', '否则', '细分', '路径', '反哺', '设为', '吾有一数谓之'
                ],

                typeKeywords: [
                    'boolean', 'double', 'byte', 'int', 'short', 'char', 'void', 'long', 'float'
                ],

                operators: [
                    '=', '>', '<', '!', '~', '?', ':', '==', '<=', '>=', '!=',
                    '&&', '||', '++', '--', '+', '-', '*', '/', '&', '|', '^', '%',
                    '<<', '>>', '>>>', '+=', '-=', '*=', '/=', '&=', '|=', '^=',
                    '%=', '<<=', '>>=', '>>>=',
                    '次方', '加'
                ],

                // we include these common regular expressions
                symbols: /[=><!~?:&|+\-*\/\^%\u4e00-\u9fa5]+/,

                // C# style strings
                escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,

                // The main tokenizer for our languages
                tokenizer: {

                    root: [

                        // numbers
                        [/\d*\.\d+([eE][\-+]?\d+)?/, 'number.float'],
                        [/0[xX][0-9a-fA-F]+/, 'number.hex'],
                        [/[\d一二三四五六七八九零十百千万亿]+/, 'number'],

                        // identifiers and keywords
                        [/[a-z_\u4e00-\u9fa5][\w\u4e00-\u9fa5$]*/, {
                            cases: {
                                '@typeKeywords': 'keyword',
                                '@keywords': 'keyword',
                                '@default': 'identifier'
                            }
                        }],
                        [/[A-Z][\w\$]*/, 'type.identifier'], // to show class names nicely

                        // whitespace
                        {
                            include: '@whitespace'
                        },

                        // delimiters and operators
                        [/[{}()\[\]]/, '@brackets'],
                        [/[<>](?!@symbols)/, '@brackets'],
                        [/@symbols/, {
                            cases: {
                                '@operators': 'operator',
                                '@default': ''
                            }
                        }],

                        // @ annotations.
                        // As an example, we emit a debugging log message on these tokens.
                        // Note: message are supressed during the first load -- change some lines to see them.
                        [/@\s*[a-zA-Z_\$][\w\$]*/, {
                            token: 'annotation',
                            log: 'annotation token: $0'
                        }],

                        // delimiter: after number because of .\d floats
                        [/[;,.]/, 'delimiter'],

                        // strings
                        [/"([^"\\]|\\.)*$|'([^'\\]|\\.)*$/, 'string.invalid'], // non-teminated string
                        [/"|'/, {
                            token: 'string.quote',
                            bracket: '@open',
                            next: '@string'
                        }],

                        // characters
                        [/'[^\\']'/, 'string'],
                        [/(')(@escapes)(')/, ['string', 'string.escape', 'string']],
                        [/'/, 'string.invalid']
                    ],

                    comment: [
                        [/[^\/*]+/, 'comment'],
                        [/\/\*/, 'comment', '@push'], // nested comment
                        ["\\*/", 'comment', '@pop'],
                        [/[\/*]/, 'comment']
                    ],

                    string: [
                        [/[^\\"']+/, 'string'],
                        [/@escapes/, 'string.escape'],
                        [/\\./, 'string.escape.invalid'],
                        [/"|'/, {
                            token: 'string.quote',
                            bracket: '@close',
                            next: '@pop'
                        }]
                    ],

                    whitespace: [
                        [/[ \t\r\n]+/, 'white'],
                        [/\/\*/, 'comment', '@comment'],
                        [/\/\/.*$/, 'comment'],
                        [/##.*$/, 'comment'],
                    ],
                },
            });

            // Register a completion item provider for the new language
            monaco.languages.registerCompletionItemProvider('dsl', {
                provideCompletionItems: () => {
                    var suggestions = [{
                        label: 'simpleText',
                        kind: monaco.languages.CompletionItemKind.Text,
                        insertText: 'simpleText'
                    }, {
                        label: 'testing',
                        kind: monaco.languages.CompletionItemKind.Keyword,
                        insertText: 'testing(${1:condition})',
                        insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet
                    }, {
                        label: 'ifelse',
                        kind: monaco.languages.CompletionItemKind.Snippet,
                        insertText: ['if (${1:condition}) {', '\t$0', '} else {', '\t', '}'].join('\n'),
                        insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
                        documentation: 'If-Else Statement'
                    }];
                    return {
                        suggestions: suggestions
                    };
                }
            });

            $.get("/git/grammar-repo/opendsl/0.4/demo/demo.dsl", function(dslCode) {
                dslCodeEditor = monaco.editor.create(document.getElementById('dslCode'), {
                    value: dslCode,
                    language: 'dsl',
                    theme: 'dslTheme',
                    scrollBeyondLastLine: false,
                });
            })
        });

        function getCode() {
            return dslCodeEditor.getValue();
        }

        function getDsl() {

            var dsl = window.parent.getDsl()
            return dsl;
        }

        function parse() {
            setIframe("ast");
            parseAST();
        }

        function parseAST() {
            parseASTAndDo(function(result) {
                // $("button").attr("disabled", false);

                var astJsonText = result.ast;
                if (!astJsonText) {
                    alert("解析AST错误: " + result.message);
                    return;
                }

                var dataJson = JSON.parse(astJsonText);
                callAction("setEditorValue", dataJson.ast);
            })
        }

        function execute() {

            setIframe("result");

            parseASTAndDo(function(result) {

                var astText = result.ast;
                if (!astText) {
                    alert("执行代码错误: " + JSON.stringify(result));
                    return;
                }

                var dataJson = JSON.parse(astText);
                //执行生成js代码的情景使用
                // var value = eval(result.js);
                usePrintlnFlag = false;
                //默认场景使用
                var value = executeAST(dataJson.ast);
                if (usePrintlnFlag) {
                    return;
                }

                var resultText = formatValue(value);
                callAction("setEditorResultValue", resultText);
            })
        }

        function formatValue(value) {
            var resultText;
            if (null === value) {
                resultText = "null";
            } else if ("string" === typeof(value)) {
                resultText = value;
            } else if ("object" === typeof(value)) {
                resultText = JSON.stringify(value);
            } else if ("boolean" === typeof(value)) {
                resultText = value;
            } else if (isNaN(value)) {
                resultText = value;
            } else {
                resultText = Math.round(value * 10000000000) / 10000000000;
            }
            return resultText;
        }

        var usePrintlnFlag = false;

        function executeAST(ast) {

            var input = {};
            var context = {};
            var runtime = new OpenDSLRuntime.default(context);
            runtime.registerSystemFunction({
                println: function(text) {
                    usePrintlnFlag = true;
                    if (undefined === text) {
                        text = "";
                    }
                    var resultText = formatValue(text) + "\r\n";
                    callAction("appendEditorResultValue", resultText);
                    console.info(resultText);
                    return null;
                },
                abs: function(number) {
                    return Math.abs(number);
                },
                math: Math
            });
            runtime.registerCheck(function(output) {
                if (output && "object" === typeof(output) && output.message) {
                    // alert(output.message);
                    return false;
                }
                return true;
            })
            runtime.prepare(ast);
            var localContext = {};
            var output = runtime.execute(ast, input, localContext);
            return output;
        }

        function parseASTAndDo(action) {
            var code = getCode();
            var dsl = getDsl();
            var param = {
                code: encodeURI(code),
                dsl: encodeURI(dsl)
            }
            $("button").attr("disabled", true);
            $.post("/dsl/parse/to/ast", param, function(result) {
                $("button").attr("disabled", false);
                if (action) {
                    action(result);
                }
            })
        }

        var astEditor = null;;

        function registerASTEditor(astEditor) {
            window.astEditor = astEditor;
        }

        var callAction = null;

        function registerCallAction(callAction) {
            window.callAction = callAction;
        }

        function showHelp() {
            setIframe("help");
        }

        var iframeLocationMap = {
            // "help":"help."
        }

        function setIframe(name) {
            window.frames[0].location.href = name + ".html";
        }
    </script>
</body>

</html>